package mcom.xmks.bluetooth.mesh;

import android.content.Intent;
import android.os.Handler;
import android.os.Looper;
import android.text.method.ScrollingMovementMethod;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.appcompat.app.ActionBar;

import com.google.android.material.progressindicator.LinearProgressIndicator;

import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Locale;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

import cn.wch.meshlib.core.MeshServer;
import cn.wch.meshlib.ota.IOTAUpdateProgress;
import cn.wch.meshlib.ota.ImageInfo;
import io.reactivex.Emitter;
import io.reactivex.Observable;
import io.reactivex.ObservableOnSubscribe;
import io.reactivex.Observer;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
import mcom.xmks.bluetooth.mesh.base.AbstractActivity;
import mcom.xmks.bluetooth.mesh.constant.IntentConstant;
import mcom.xmks.bluetooth.mesh.constant.WidgetConstant;
import mcom.xmks.bluetooth.mesh.core.DataModel;
import mcom.xmks.bluetooth.mesh.ota.FileListDialog;
import mcom.xmks.bluetooth.mesh.ota.UpdateFileResolver;
import mcom.xmks.bluetooth.mesh.utils.LogTool;
import mcom.xmks.bluetooth.mesh.utils.TimeUtil;

public class NodeOTAActivity extends AbstractActivity {
private int nodeAddress;
private String nodeName;
private ImageInfo imageInfo=null;
    private File updateFile=null;
    private Handler handler=new Handler(Looper.getMainLooper());
    private int speedOldTemp = 0;
    private int speedNewTemp = 0;
    private ScheduledExecutorService monitor =null;
    private Intent intent;
    private Button btnGetInfo,btnSelectFile,btnStart;
    private TextView tvLog,tvChipType,tvImageSize,tvBlockSize,tvUpdateFIle,tvSpeed;
    private LinearProgressIndicator pbProgress;
    @Override
    protected void setView() {
        setContentView(R.layout.activity_node_otaactivity);
        intent=getIntent();
        if(intent!=null){
            nodeAddress=intent.getIntExtra(IntentConstant.NODE_NET_ADDRESS,-1);
        }
        ActionBar actionBar = getSupportActionBar();
        if(actionBar!=null){
            actionBar.setTitle("OTA升级");
            actionBar.setSubtitle(String.format(Locale.getDefault(),"节点地址:%04X",nodeAddress));
            actionBar.setHomeButtonEnabled(true);  //启用返回按钮
            actionBar.setDisplayHomeAsUpEnabled(true); //显示返回按钮
        }
        btnGetInfo=this.findViewById(R.id.btn_get_info);
        btnSelectFile=this.findViewById(R.id.btn_select_file);
        btnStart=this.findViewById(R.id.btn_start);
        tvLog=this.findViewById(R.id.tv_log);
        tvChipType=this.findViewById(R.id.tvChipType);
        tvImageSize=this.findViewById(R.id.tvImageSize);
        tvBlockSize=this.findViewById(R.id.tvBlockSize);
        tvUpdateFIle=this.findViewById(R.id.tvUpdateFIle);
        pbProgress=this.findViewById(R.id.pb_progress);
        tvSpeed=this.findViewById(R.id.tvSpeed);
    }

    @Override
    protected void initWidget() {
        btnGetInfo.setOnClickListener(click);
        btnSelectFile.setOnClickListener(click);
        btnStart.setText(WidgetConstant.START);
        btnStart.setOnClickListener(click);
        tvLog.setMovementMethod(ScrollingMovementMethod.getInstance());
        startSpeedMonitor();
    }
    private View.OnClickListener click= v -> {
        switch (v.getId()){
            case R.id.btn_get_info:
                getImageInfo();
                break;
            case R.id.btn_select_file:
                selectFile();
                break;
            case R.id.btn_start:
                switch (((Button)v).getText().toString()){
                    case WidgetConstant.START:
                        startOTAUpdate();
                        break;
                    case WidgetConstant.CANCEL:
                        cancelOTAUpdate();
                        break;
                }
                break;
        }
    };

    @Override
    public boolean onOptionsItemSelected(@NonNull MenuItem item) {
       switch (item.getItemId()){
           case  android.R.id.home:
               onBackPressed();
               break;
       }
        return super.onOptionsItemSelected(item);
    }

    @Override
    public void onBackPressed() {
        super.onBackPressed();
        MeshServer.getInstance().cancelOTAUpdate();
        stopSpeedMonitor();
    }
    private void getImageInfo() {
        if(!MeshServer.getInstance().isConnected()){
            showToast("未连接蓝牙设备");
            return;
        }
        if( nodeAddress<0){
            LogTool.d("node address is null or negative");
            return;
        }
        DataModel.getInstance().rxjavaExecute(this, "获取Image信息", new DataModel.CallbackRunnable() {
            @Override
            public ImageInfo onRunnable()  {
                try {
                    return MeshServer.getInstance().otaQueryNodeImageInfo(nodeAddress);
                } catch (Exception e) {
                    return null;
                }
            }
        }, new Emitter<ImageInfo>() {
            @Override
            public void onNext(ImageInfo info) {
                NodeOTAActivity.this.imageInfo=info;
                tvChipType.setText(info.getChipType().getDescription());
                tvImageSize.setText(String.format(Locale.getDefault(),"%d",info.getImageSize()));
                tvBlockSize.setText(String.format(Locale.getDefault(),"%d",info.getBlockSize()));
            }
            @Override
            public void onError(Throwable e) {
                showToast(e.getMessage());
            }
            @Override
            public void onComplete() {
            }
        });
//        try {
//          ImageInfo  imageInfo= MeshServer.getInstance().otaQueryNodeImageInfo(nodeAddress);
//            this.imageInfo=imageInfo;
//            tvChipType.setText(imageInfo.getChipType().getDescription());
//            tvImageSize.setText(String.format(Locale.getDefault(),"%d",imageInfo.getImageSize()));
//            tvBlockSize.setText(String.format(Locale.getDefault(),"%d",imageInfo.getBlockSize()));
//        } catch (Exception e) {
//            e.printStackTrace();
//            showToast(e.getMessage());
//        }
    }
    private void selectFile() {
        File privateFolder = UpdateFileResolver.getPrivateFolder(this);
        if(!privateFolder.exists()){
            showToast("OTA文件夹不存在");
        }else{
            File[] files= privateFolder.listFiles((dir, name) ->
                    name.endsWith(".bin")||name.endsWith(".BIN")||
                            name.endsWith(".hex") || name.endsWith(".HEX"));
            if (files == null || files.length==0) {
                showToast("未发现升级文件");
            }else{
                FileListDialog dialog=FileListDialog.newInstance(new ArrayList(Arrays.asList(files)));
                dialog.setCancelable(false);
                dialog.show(getSupportFragmentManager(), FileListDialog.class.getName());
                dialog.setOnChooseListener( file ->{
                        updateFile = file;
                    tvUpdateFIle.setText(String.format(Locale.US, "%s", file.getName()));
                });
            }
        }
    }
    private void startOTAUpdate() {
        if(!MeshServer.getInstance().isConnected()){
            showToast("未连接蓝牙设备");
            return;
        }
        if(nodeAddress<0){
            LogTool.d("node address is null or negative");
            return;
        }
        if(imageInfo==null){
            showToast("请先获取硬件信息");
            return;
        }
        if(updateFile==null){
            showToast("请先选择升级文件");
            return;
        }
        Observable.create((ObservableOnSubscribe<String>) emitter -> {
            try {
                MeshServer.getInstance().startOTAUpdate(nodeAddress, imageInfo, updateFile, new IOTAUpdateProgress() {
                    @Override
                    public void onUpdateStart() {
                        resetSpeedCount();
                        emitter.onNext("start update!");
                    }
                    @Override
                    public void onUpdateProgress(int current, int total) {
                        updateSpeedCount(current);
                        updateProgress(current, total);
                    }
                    @Override
                    public void onUpdateFinish() {
                        emitter.onNext("update finish!");
                    }
                    @Override
                    public void onVerifyStart() {
                        resetSpeedCount();
                        emitter.onNext("start verify!");
                    }
                    @Override
                    public void onVerifyProgress(int current, int total) {
                        updateSpeedCount(current);
                        updateProgress(current, total);
                    }
                    @Override
                    public void onVerifyFinish() {
                        emitter.onNext("verify finish!");
                    }
                    @Override
                    public void onEnd() {
                        emitter.onNext("end!");
                        emitter.onComplete();
                    }
                    @Override
                    public void onCancel() {
                        emitter.onError(new Throwable("cancel OTA update!"));
                    }
                    @Override
                    public void onError(String message) {
                        emitter.onError(new Throwable(message));
                    }
                    @Override
                    public void onInformation(String message) {
                        emitter.onNext(message);
                    }
                });
            } catch (Exception e) {
                emitter.onNext(e.getMessage());
                emitter.onError(new Throwable(e.getMessage()));
            }
        }).subscribeOn(Schedulers.newThread())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Observer<>() {
                    @Override
                    public void onSubscribe(Disposable d) {
                        clearLog();
                        updateProgress(0,100);
                        btnStart.setText(WidgetConstant.CANCEL);
                    }

                    @Override
                    public void onNext(String s) {
                        updateLog(s);
                    }
                    @Override
                    public void onError(Throwable e) {
                        resetSpeedCount();
                        showToast(e.getMessage());
                        btnStart.setText(WidgetConstant.START);
                        LogTool.d(e.getMessage());
                        updateLog(e.getMessage());
                    }

                    @Override
                    public void onComplete() {
                        resetSpeedCount();
                        updateLog("OTA update success!");
                        btnStart.setText(WidgetConstant.START);
                    }
                });
    }

    /**
     * 取消OTA更新
     */
    private void cancelOTAUpdate() {
        MeshServer.getInstance().cancelOTAUpdate();
    }

    /**
     * 更新进度
     * @param current 当前值
     * @param total 总值
     */
    private void updateProgress(int current,int total){
        handler.post(() -> {
            pbProgress.setProgress(current * 100 / total);
        });
    }

    /**
     * 更新日志
     * @param message 日志内容
     */
    private void updateLog(String message){
        handler.post(() -> {
             tvLog.append(TimeUtil.getCurrentTime() + ">> " + message+"\r\n");
            int offset  =  tvLog.getLineCount() *  tvLog.getLineHeight();
            //int maxHeight = usbReadValue.getMaxHeight();
            int height = tvLog.getHeight();
            //USBLog.d("offset: "+offset+"  maxHeight: "+maxHeight+" height: "+height);
            if (offset > height) {
                //USBLog.d("scroll: "+(offset - usbReadValue.getHeight() + usbReadValue.getLineHeight()));
                 tvLog.scrollTo(0, offset -  tvLog.getHeight() +  tvLog.getLineHeight());
            }
        });
    }

    /**
     * 清除日志
     */
    private void clearLog() {
        handler.post (()->{
             tvLog.setText("");
            tvLog.scrollTo(0, 0);
        });
    }

    /**
     * 启动速度监视器
     */
    private void startSpeedMonitor() {
        resetSpeedCount();
        if(monitor==null){
            monitor= Executors.newScheduledThreadPool(3);
        }
        monitor.scheduleWithFixedDelay(()-> {
            handler.post (()->{
                int count = speedNewTemp - speedOldTemp;
                speedOldTemp = speedNewTemp;
                if (count >= 0) {
                    tvSpeed.setText(String.format(Locale.US, "速度:%d字节/秒", count));
                }
            });
        },100,1000, TimeUnit.MILLISECONDS);
    }

    /**
     * 停止速度监视器
     */
    private void stopSpeedMonitor() {
        monitor.shutdown();
        monitor=null;
        resetSpeedCount();
    }

    /**
     * 重置速度计数
     */
    private void resetSpeedCount() {
        speedOldTemp = 0;
        speedNewTemp = 0;
    }
    /**
     * 更新速度计数
     * @param newCount
     */
    private void updateSpeedCount(int newCount) {
        speedNewTemp = newCount;
    }
}